当前等级
{{myMembership.level==='premium'?'高级会员':myMembership.level==='normal'?'普通会员':'免费用户'}}
到期:{{fmtTime(myMembership.expires_at)}}
配额使用
热点查询:{{myMembership.hotspot_used||0}}/{{myMembership.hotspot_limit||3}}
跑腿发布:{{myMembership.errand_used||0}}/{{myMembership.errand_limit||0}}
权益对比
功能
免费
普通
高级
看帖/发帖 ✅ ✅ ✅
点赞/差评 ✅ ✅ ✅
查周边热点 3次/天 30次/月 ♾️
发供给 扣豆 扣豆 扣豆
发跑腿 ❌ 10次/月 ♾️
接单 ✅ ✅ ✅
守护出行 ✅ ✅ ✅
守护关系 1组 5组 ♾️
轨迹回放 ❌ ✅ ✅
走散预警 ❌ ❌ ✅
多人团 3人 10人 ♾️
赚豆方式
📅 每日签到 +1豆
✍️ 发帖 +1豆
👍 被点赞 +1信誉
🤝 完成供给 +2豆
暂无帖子
{{p.content}}
{{fmtTime(p.created_at)}}
👍 {{p.likes}} 👎 {{p.dislikes}}
🤝 供给 {{p.reward_amount}}豆
当前余额
{{myInfo.beans||0}} 豆
暂无记录
{{l.reason}}
{{fmtTime(l.created_at)}}
{{l.amount>0?'+':''}}{{l.amount}}
暂无举报记录
{{fmtTime(r.created_at)}}
下架帖子 删除记录
原因: {{r.reason}}
帖子: {{r.post_content||'已删除'}}
举报人:{{r.reporter_nick||'未知'}} | 帖子作者:{{r.post_owner_nick||'未知'}}
← 返回 消息中心 {{unreadCount}} 全部已读
暂无消息
{{n.title||'系统消息'}}
{{n.content}}
{{fmtTime(n.created_at)}}
同意
拒绝
已处理
暂无举报记录
{{fmtTime(r.created_at)}}
原因: {{r.reason}}
帖子: {{r.post_content||'已删除'}}
← 返回
隐私政策
更新日期:2026年6月6日
一、我们收集哪些信息
1. 注册信息:手机号(用于实名认证)、昵称、密码。
2. 位置信息:仅在您主动开启定位或使用"出行"功能时获取,且仅在出行期间临时共享,出行结束即清除。
3. 帖子内容:您发布的文字和图片。
二、我们如何使用信息
1. 手机号仅用于账号注册和登录验证,不会对外展示。
2. 位置信息仅用于"出行报备"功能,由出行者主动发起共享,到达目的地或出行结束后自动停止共享并清除轨迹数据。
3. 帖子内容用于社区展示,帖子保留6个月后自动清除。
三、信息共享与披露
1. 位置共享:仅在出行期间,同行团员及已绑定的守护人可查看位置,出行结束即不可见。
2. 我们不会将您的个人信息出售或分享给第三方。
3. 依法配合公安机关调取涉案信息时,我们将锁定并保留相关数据。
四、数据保留期限
1. 位置日志:出行结束即清除。
2. 帖子内容:保留6个月后自动清除,涉案帖子锁定保存。
3. 系统日志:保留6个月。
4. 实名信息:服务终止后至少保留2年。
五、您的权利
1. 您可随时关闭定位、结束出行、解除守护关系。
2. 出行结束后,所有位置数据自动清除。
3. 您可举报违规内容,我们将依法处理。
六、守护模式说明
守护模式遵循"出行报备"原则:由出行者主动发起位置共享,而非被动被追踪。控制权始终在出行者手中。
· 出行者主动创建/加入出行后,位置才对守护人可见
· 到达目的地后,出行自动结束,位置数据清除
· 任何团员均可结束出行
· 出行结束后,守护人无法再获取位置信息
七、联系我们
如有疑问,请通过App内反馈功能联系我们。
← 返回
用户协议
更新日期:2026年6月6日
一、服务说明
卷十息是一款基于位置的社区服务应用,提供周边动态、出行报备、悬赏供给等功能。使用本应用即表示您同意本协议。
二、用户行为规范
1. 不得发布违法、暴力、色情、虚假、侵权等内容。
2. 不得利用本应用进行骚扰、诈骗等违法行为。
3. 违规内容将被删除,严重者将封禁账号并配合公安机关处理。
三、实名认证
根据《网络安全法》要求,用户需通过手机号完成实名注册。后台实名、前台自愿。
四、位置信息
位置功能为可选增强功能,不强制索取。出行报备功能由用户主动发起,出行结束自动停止共享。
五、免责声明
1. 用户发布的内容由用户自行负责。
2. 悬赏交易由双方自行协商,平台不承担担保责任。
3. 因不可抗力导致服务中断,平台不承担责任。
{{(p.nickname||'?')[0]}}
{{p.nickname}}
{{fmtTime(p.created_at)}}
🤝供给
{{fmtDist(p.distance)}}
⋯
{{p.content}}
{{p.bounty_beans}}豆
{{(p.bounty_status==='claimed'?'已接单':p.bounty_status==='done'?'已完成':'供给中')}}
{{posts.length>=20?'下拉加载更多':'没有更多了'}}
发帖
← 退出
帖子
我需要帮助
我提供
{{pubContent.length}}/500
⚠️ 请勿委托购买处方药,线下交易由双方自行协商,平台不参与资金往来
诚意豆(必填)
{{bountyBeans}} 豆
发布需求需放置诚意豆,完成后自动转给帮忙人,避免虚假需求
服务定价
{{errandReward}} 豆
需求方完成确认后自动转给你,体现你的服务价值
💡 描述你能提供的服务,如:修电器、通水管、开锁、代买等
⚠️ 请勿代购处方药,交易由双方自行协商,平台不参与资金往来
图片
{{locText||'定位中'}}
发布
帖子保留6个月后自动清除
出行
守护
创建出行
加入出行
{{activeTrip.to_name||'未知目的地'}} {{activeTrip.status==='ongoing'?'进行中':activeTrip.status==='arrived'?'已到达':'已结束'}}
邀请码: {{activeTrip.join_code}} 复制
{{fmtTime(activeTrip.created_at)}} 出发 · 约{{Math.round(activeTrip.duration/60*10)/10}}小时
团队状态: {{groupView.dispersion==='together'?'在一起':groupView.dispersion==='nearby'?'较近':'已走散'}}
{{(m.nickname||'?')[0]}}
{{m.nickname||'团员'}}
{{fmtDist(m.distance)}}
查看位置 群聊 退出 结束出行 删除行程
出行记录
{{t.to_name||'出行'}} {{t.status==='arrived'?'已到达':'已结束'}}
{{fmtTime(t.created_at)}}
{{activeTrip.to_name||'未知目的地'}} 进行中
{{fmtTime(activeTrip.created_at)}} 出发
收到的邀请
{{(inv.inviter_name||'?')[0]}}
{{inv.inviter_name||'用户'}}
邀请你守护「{{inv.destination||'出行'}}」
同意
拒绝
我守护的
{{(g.ward_name||'?')[0]}}
{{g.ward_name||'被守护人'}}
绑定于 {{fmtTime(g.created_at)}}
解除 📍 查看位置 💬 私聊
守护我的
{{(g.guardian_name||'?')[0]}}
{{g.guardian_name||'守护人'}}
绑定于 {{fmtTime(g.created_at)}}
解除 💬 私聊
🤝 身边供给
← 退出
🙋 我需要
💪 我提供
定价{{b.reward_amount||0}}豆 {{b.beans||0}}豆
{{b.status==='open'?'供给中':b.status==='claimed'?'已接单':'已完成'}}
{{b.content}}
{{b.nickname||'匿名'}} · {{fmtTime(b.created_at)}} · {{fmtDist(b.distance)}}
{{b.bounty_type==='errand'?'响应人':'帮忙人'}}: {{b.claim_nickname}}
{{b.bounty_type==='errand'?'我来响应':'我来帮'}}
💬对话
确认完成
匿名评价
已评价✅
取消
← 退出
{{(myInfo.nickname||'?')[0]}}
{{myInfo.nickname||'未登录'}}
{{myInfo.mood||'正面'}} · 注册{{fmtDays(myInfo.created_at)}}天
{{myInfo.post_count||0}}
发帖
{{myInfo.like_count||0}}
获赞
{{myInfo.reputation||0}}
信誉
🔔 消息中心
{{unreadCount}} ›
每日签到
{{signedToday?'已签到✅':'签到+1豆'}} ›
会员中心
›
我的帖子
›
举报管理
›
豆明细
›
我的举报
›
📍 生活半径 🔒会员
500米
1公里
3公里
5公里
退出登录
身边
发布
出行
供给
我的
匿名评价
评价完全匿名,对方无法看到是谁评的
评分
取消
提交评价
{{(repU.nickname||'?')[0]}}
{{repU.nickname||'匿名'}}
{{repU.membership.level==='premium'?'高级会员':'普通会员'}}
免费用户
{{repU.mood||'正面'}}
{{repU.reputation||0}}
信誉值
{{repU.received_likes||0}}
👍 获赞
{{repU.received_dislikes||0}}
👎 获差评
{{repU.hotspot_received||0}}
🔥 推荐上热门
{{repTip}} ✕
📥 我获得的
信誉值 {{repU.reputation||0}}
获赞 {{repU.received_likes||0}}
获差评 {{repU.received_dislikes||0}}
被举报 {{repU.reported_count||0}}
被人帮过 {{repU.help_received||0}} 次
爽约/取消 {{repU.no_shows||0}} 次
获得热力推荐 {{repU.hotspot_received||0}} 次
📤 我给出的
给出赞 {{repU.given_likes||0}}
给出差评 {{repU.given_dislikes||0}}
给出热力推荐 {{repU.given_hotspots||0}}
给出举报 {{repU.given_reports||0}}
帮助过别人 {{repU.help_given||0}} 次
💡 信誉说明
信誉值体现社区成员的人品和可信度。帮人+1,获赞+1;差评-2,被举报-1,爽约扣信誉。信誉越高,越容易获得邻里信任和供给机会。
关闭
邀请守护人 ×
输入对方手机号,邀请成为你的守护人
发送邀请
大市场
之前插入
// 需要:店铺详情页、下单页、消费码页、商家中心
// 所有DOM放在#app外面(与marketOverlay同策略)
// === 店铺详情覆盖层 ===
document.addEventListener('DOMContentLoaded', function() {
// 店铺详情页DOM(插在merchantApplyPopup之后)
var shopHTML = '
';
shopHTML += '
';
shopHTML += '← 返回 ';
shopHTML += '店铺 ';
shopHTML += ' ';
shopHTML += '
';
shopHTML += '
';
shopHTML += '
';
shopHTML += '到店下单 ';
shopHTML += '
';
shopHTML += '
';
// 下单页
shopHTML += '
';
shopHTML += '
';
shopHTML += '← 返回 ';
shopHTML += '确认下单 ';
shopHTML += ' ';
shopHTML += '
';
shopHTML += '
';
shopHTML += '
';
shopHTML += '
合计:¥0 (以到店实际价为准)
';
shopHTML += '
提交订单 ';
shopHTML += '
';
shopHTML += '
';
// 消费码页
shopHTML += '
';
shopHTML += '
';
shopHTML += '← 返回 ';
shopHTML += '消费码 ';
shopHTML += ' ';
shopHTML += '
';
shopHTML += '
';
shopHTML += '
';
// 商家中心覆盖层
shopHTML += '
';
shopHTML += '
';
shopHTML += '← 返回 ';
shopHTML += '🏪 商家中心 ';
shopHTML += ' ';
shopHTML += '
';
shopHTML += '
';
shopHTML += '
';
// 插入DOM
var applyPopup = document.getElementById('merchantApplyPopup');
if (applyPopup && applyPopup.nextSibling) {
var temp = document.createElement('div');
temp.innerHTML = shopHTML;
while (temp.firstChild) {
applyPopup.parentNode.insertBefore(temp.firstChild, applyPopup.nextSibling);
}
} else {
document.body.insertAdjacentHTML('beforeend', shopHTML);
}
});
// === 全局变量 ===
var shopMerchant = null;
var shopItems = [];
var orderCart = [];
var currentOrder = null;
var merchantCenterData = null;
// === 店铺详情 ===
function openShop(merchantId) {
var vm = window.__vueApp;
if (!vm || !vm.token) { if(typeof vant!=='undefined') vant.showToast('请先登录'); return; }
document.getElementById('marketOverlay').style.display = 'none';
document.getElementById('shopOverlay').style.display = 'flex';
document.getElementById('shopContent').innerHTML = '
加载中...
';
document.getElementById('shopBottomBar').style.display = 'none';
fetch('/api/merchants/' + merchantId + '/detail', {headers: {'Authorization': 'Bearer ' + vm.token}})
.then(function(r) { return r.json(); })
.then(function(d) {
shopMerchant = d.merchant;
shopItems = d.merchant.items || [];
document.getElementById('shopTitle').textContent = shopMerchant.name || '店铺';
document.getElementById('shopBottomBar').style.display = 'block';
renderShop();
})
.catch(function() {
document.getElementById('shopContent').innerHTML = '
加载失败
';
});
}
function renderShop() {
var m = shopMerchant;
var items = shopItems;
var catMap = {eat:'吃🍜',wear:'穿👕',live:'住🏠',use:'用🛒',go:'行🚗'};
var html = '';
// 顶部信息
html += '
';
if (m.cover_image) {
html += '
';
}
html += '
';
html += '' + (m.name||'') + ' ';
if (m.verified) html += '已认证 ';
html += '
';
html += '
';
html += '' + (catMap[m.category]||m.category||'') + ' ';
if (m.rating) html += '⭐' + parseFloat(m.rating).toFixed(1) + ' ';
if (m.avg_price) html += '人均¥' + parseFloat(m.avg_price).toFixed(0) + ' ';
html += '
';
if (m.hours) html += '
🕐 ' + m.hours + '
';
if (m.address) html += '
📍 ' + m.address + '
';
if (m.phone) html += '
';
if (m.description) html += '
' + m.description + '
';
if (m.support_invoice) html += '
🧾 支持开具发票
';
html += '
';
// 商品列表
if (items.length > 0) {
html += '
';
html += '
🍽️ 商品/服务
';
// 分类Tab
var cats = [];
var catSet = {};
items.forEach(function(it) {
if (it.category && !catSet[it.category]) { catSet[it.category] = true; cats.push(it.category); }
});
if (cats.length > 1) {
html += '
';
html += '
全部
';
cats.forEach(function(c) {
html += '
' + c + '
';
});
html += '
';
}
// 商品卡片
html += '
';
items.forEach(function(it) {
html += '
';
if (it.image) {
html += '
';
} else {
html += '
🍽
';
}
html += '
';
html += '
' + (it.name||'') + '
';
if (it.description) html += '
' + it.description + '
';
var specs = it.specs || {};
var specKeys = Object.keys(specs);
if (specKeys.length > 0) {
html += '
';
specKeys.forEach(function(k) { html += k + ':' + specs[k] + ' '; });
html += '
';
}
html += '
';
html += '¥' + parseFloat(it.price).toFixed(2) + ' ';
html += '+ 加入 ';
html += '
';
html += '
';
});
html += '
';
} else {
html += '
';
}
document.getElementById('shopContent').innerHTML = html;
}
function filterShopItems(cat) {
document.querySelectorAll('.shop-cat-tab').forEach(function(el) {
if (el.getAttribute('data-cat') === cat) {
el.style.background = '#ff6034'; el.style.color = '#fff'; el.style.fontWeight = '600';
} else {
el.style.background = '#f0f0f0'; el.style.color = '#666'; el.style.fontWeight = 'normal';
}
});
document.querySelectorAll('.shop-item-card').forEach(function(el) {
if (!cat || el.getAttribute('data-category') === cat) {
el.style.display = 'flex';
} else {
el.style.display = 'none';
}
});
}
function closeShop() {
document.getElementById('shopOverlay').style.display = 'none';
document.getElementById('shopBottomBar').style.display = 'none';
shopMerchant = null;
shopItems = [];
orderCart = [];
}
// === 下单 ===
function addToCart(itemId) {
var item = shopItems.find(function(i) { return i.id === itemId; });
if (!item) return;
var existing = orderCart.find(function(c) { return c.item_id === itemId; });
if (existing) {
existing.quantity += 1;
} else {
orderCart.push({ item_id: item.id, name: item.name, price: parseFloat(item.price), quantity: 1, specs: item.specs || {} });
}
if(typeof vant!=='undefined') vant.showToast(item.name + ' 已加入');
}
function startOrder() {
if (!orderCart.length) {
// 没有选商品也可以下单(直接到店)
}
document.getElementById('orderOverlay').style.display = 'flex';
renderOrder();
}
function renderOrder() {
var html = '';
html += '
';
html += '
' + (shopMerchant.name||'') + '
';
if (orderCart.length > 0) {
orderCart.forEach(function(it, idx) {
html += '
';
html += '
' + it.name + '
';
html += '
¥' + it.price.toFixed(2) + '
';
html += '
';
html += '- ';
html += '' + it.quantity + ' ';
html += '+ ';
html += '
';
});
} else {
html += '
未选商品,直接到店消费
';
}
html += '
';
// 备注
html += '
';
html += '
备注
';
html += '';
html += '
';
// 提示
html += '
💡 本平台不收款,到店实际价格为准 下单后生成消费码,到店出示即可
';
document.getElementById('orderContent').innerHTML = html;
updateOrderTotal();
}
function changeQty(idx, delta) {
orderCart[idx].quantity += delta;
if (orderCart[idx].quantity <= 0) orderCart.splice(idx, 1);
renderOrder();
}
function updateOrderTotal() {
var total = 0;
orderCart.forEach(function(it) { total += it.price * it.quantity; });
document.getElementById('orderTotal').textContent = '¥' + total.toFixed(2);
}
function closeOrderPage() {
document.getElementById('orderOverlay').style.display = 'none';
}
function submitOrder() {
var vm = window.__vueApp;
if (!vm || !vm.token) { if(typeof vant!=='undefined') vant.showToast('请先登录'); return; }
if (!shopMerchant) return;
var items = orderCart.map(function(it) {
return { item_id: it.item_id, name: it.name, price: it.price, quantity: it.quantity, specs: it.specs };
});
var remark = '';
var remEl = document.getElementById('orderRemark');
if (remEl) remark = remEl.value.trim();
var btn = document.getElementById('submitOrderBtn');
btn.disabled = true;
btn.textContent = '提交中...';
fetch('/api/merchant-orders', {
method: 'POST',
headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer ' + vm.token},
body: JSON.stringify({ merchant_id: shopMerchant.id, items: items, remark: remark })
}).then(function(r) { return r.json(); }).then(function(d) {
btn.disabled = false;
btn.textContent = '提交订单';
if (d.error) { if(typeof vant!=='undefined') vant.showToast(d.error); return; }
currentOrder = d.order;
orderCart = [];
closeOrderPage();
closeShop();
showConsumeCode(d.order);
}).catch(function() {
btn.disabled = false;
btn.textContent = '提交订单';
if(typeof vant!=='undefined') vant.showToast('下单失败');
});
}
// === 消费码页面 ===
function showConsumeCode(order) {
document.getElementById('consumeCodeOverlay').style.display = 'flex';
currentOrder = order;
renderConsumeCode();
}
function renderConsumeCode() {
var o = currentOrder;
if (!o) return;
var items = typeof o.items === 'string' ? JSON.parse(o.items) : (o.items || []);
var statusMap = {pending:'待到店', confirmed:'已核销', cancelled:'已取消', expired:'已过期'};
var statusColor = {pending:'#ff9800', confirmed:'#07c160', cancelled:'#999', expired:'#999'};
var html = '';
// 消费码大字
html += '
';
html += '
到店出示此消费码
';
html += '
' + o.consume_code + '
';
// 二维码占位(用canvas生成)
html += '
';
html += '
商家扫码或输码核销
';
html += '
';
// 订单状态
html += '
';
html += '
';
html += '订单状态 ';
html += '' + (statusMap[o.status]||o.status) + ' ';
html += '
';
html += '
订单号:' + o.order_no + '
';
html += '
下单时间:' + new Date(o.created_at).toLocaleString() + '
';
if (o.expire_at) html += '
有效期至:' + new Date(o.expire_at).toLocaleString() + '
';
html += '
';
// 商品明细
if (items.length > 0) {
html += '
';
html += '
商品明细
';
items.forEach(function(it) {
html += '
';
html += '' + it.name + ' ×' + it.quantity + ' ';
html += '¥' + (it.price * it.quantity).toFixed(2) + ' ';
html += '
';
});
html += '
';
html += '合计 ¥' + parseFloat(o.total_price).toFixed(2) + ' ';
html += '
';
html += '
以到店实际价格为准
';
html += '
';
}
// 备注
if (o.remark) {
html += '
';
html += '
备注
';
html += '
' + o.remark + '
';
html += '
';
}
// 操作按钮
if (o.status === 'pending') {
html += '
取消订单 ';
}
// 发票按钮(已核销后)
if (o.status === 'confirmed') {
if (shopMerchant && shopMerchant.support_invoice) {
html += '
🧾 申请发票 ';
}
// 导出消费清单图片
html += '
📄 导出消费清单 ';
}
document.getElementById('consumeCodeContent').innerHTML = html;
// 生成二维码
setTimeout(function() { generateQR(o.consume_code); }, 100);
}
function generateQR(code) {
var canvas = document.getElementById('qrCanvas');
if (!canvas) return;
// 简易二维码:用方格矩阵模拟(无需外部库)
var ctx = canvas.getContext('2d');
var size = 160;
var cellSize = 4;
var modules = Math.floor(size / cellSize);
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, size, size);
ctx.fillStyle = '#333';
// 用消费码生成伪随机矩阵
var seed = 0;
for (var i = 0; i < code.length; i++) seed += code.charCodeAt(i) * (i + 1);
for (var r = 0; r < modules; r++) {
for (var c = 0; c < modules; c++) {
seed = (seed * 1103515245 + 12345) & 0x7fffffff;
if (seed % 3 !== 0) {
ctx.fillRect(c * cellSize, r * cellSize, cellSize, cellSize);
}
}
}
// 中心写消费码
ctx.fillStyle = '#fff';
ctx.fillRect(size/2-30, size/2-10, 60, 20);
ctx.fillStyle = '#ff6034';
ctx.font = 'bold 14px monospace';
ctx.textAlign = 'center';
ctx.fillText(code, size/2, size/2+5);
}
function closeConsumeCode() {
document.getElementById('consumeCodeOverlay').style.display = 'none';
}
function cancelOrder(oid) {
var vm = window.__vueApp;
if (!confirm('确定取消订单?')) return;
fetch('/api/merchant-orders/' + oid + '/cancel', {
method: 'POST',
headers: {'Authorization': 'Bearer ' + vm.token}
}).then(function(r) { return r.json(); }).then(function(d) {
if (d.error) { if(typeof vant!=='undefined') vant.showToast(d.error); return; }
if(typeof vant!=='undefined') vant.showToast('订单已取消');
if (currentOrder) currentOrder.status = 'cancelled';
renderConsumeCode();
}).catch(function() { if(typeof vant!=='undefined') vant.showToast('操作失败'); });
}
function requestInvoice(oid) {
var title = prompt('发票抬头:');
if (!title) return;
var tax_no = prompt('税号(选填):') || '';
var email = prompt('接收邮箱(选填):') || '';
var vm = window.__vueApp;
fetch('/api/merchant-orders/' + oid + '/invoice', {
method: 'POST',
headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer ' + vm.token},
body: JSON.stringify({title: title, tax_no: tax_no, email: email})
}).then(function(r) { return r.json(); }).then(function(d) {
if (d.error) { if(typeof vant!=='undefined') vant.showToast(d.error); return; }
if(typeof vant!=='undefined') vant.showToast('发票申请已提交');
}).catch(function() { if(typeof vant!=='undefined') vant.showToast('申请失败'); });
}
function exportReceipt() {
// 生成消费清单图片(用canvas绘制)
var o = currentOrder;
if (!o) return;
var items = typeof o.items === 'string' ? JSON.parse(o.items) : (o.items || []);
var c = document.createElement('canvas');
c.width = 300;
c.height = 400 + items.length * 30;
var ctx = c.getContext('2d');
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, c.width, c.height);
ctx.fillStyle = '#333';
ctx.font = 'bold 18px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('卷十息 · 消费清单', 150, 30);
ctx.font = '14px sans-serif';
ctx.textAlign = 'left';
ctx.fillText('店名:' + (o.merchant_name || ''), 20, 60);
ctx.fillText('订单号:' + o.order_no, 20, 82);
ctx.fillText('消费码:' + o.consume_code, 20, 104);
ctx.fillText('时间:' + new Date(o.confirmed_at || o.created_at).toLocaleString(), 20, 126);
ctx.fillText('─────────────────────', 20, 148);
var y = 170;
items.forEach(function(it) {
ctx.fillText(it.name + ' ×' + it.quantity, 20, y);
ctx.textAlign = 'right';
ctx.fillText('¥' + (it.price * it.quantity).toFixed(2), 280, y);
ctx.textAlign = 'left';
y += 28;
});
ctx.fillText('─────────────────────', 20, y);
y += 22;
ctx.font = 'bold 16px sans-serif';
ctx.fillText('合计', 20, y);
ctx.textAlign = 'right';
ctx.fillText('¥' + parseFloat(o.total_price).toFixed(2), 280, y);
ctx.textAlign = 'center';
ctx.font = '12px sans-serif';
ctx.fillStyle = '#999';
y += 24;
ctx.fillText('以到店实际价格为准 | 卷十息 · 身边即世界', 150, y);
// 下载图片
var link = document.createElement('a');
link.download = '消费清单_' + o.consume_code + '.png';
link.href = c.toDataURL('image/png');
link.click();
}
// === 商家中心 ===
function openMerchantCenter() {
var vm = window.__vueApp;
if (!vm || !vm.token) { if(typeof vant!=='undefined') vant.showToast('请先登录'); return; }
document.getElementById('merchantCenterOverlay').style.display = 'flex';
document.getElementById('merchantCenterContent').innerHTML = '
加载中...
';
fetch('/api/merchant-orders/shop', {headers: {'Authorization': 'Bearer ' + vm.token}})
.then(function(r) { return r.json(); })
.then(function(d) {
merchantCenterData = d;
renderMerchantCenter();
})
.catch(function(e) {
// 不是商家
document.getElementById('merchantCenterContent').innerHTML = '
您还不是商家请先在大市场申请入驻
';
});
}
function renderMerchantCenter() {
var d = merchantCenterData;
if (!d || !d.merchant) return;
var m = d.merchant;
var orders = d.orders || [];
var pending = orders.filter(function(o) { return o.status === 'pending'; });
var confirmed = orders.filter(function(o) { return o.status === 'confirmed'; });
var catMap = {eat:'吃🍜',wear:'穿👕',live:'住🏠',use:'用🛒',go:'行🚗'};
var html = '';
// 店铺信息卡片
html += '
';
html += '
';
html += '' + (m.name||'') + ' ';
html += '' + (catMap[m.category]||m.category||'') + ' ';
if (m.verified) html += '已认证 ';
html += '
';
html += '
';
html += '今日待核销:' + pending.length + ' ';
html += '累计完成:' + confirmed.length + ' ';
html += '
';
html += '
';
// 核销入口
html += '
';
html += '
✅ 核销订单
';
html += '
';
html += ' ';
html += '查询 ';
html += '
';
html += '
';
html += '
';
// 待核销订单列表
html += '
';
html += '
📋 待核销订单 (' + pending.length + ')
';
if (pending.length === 0) {
html += '
暂无待核销订单
';
} else {
pending.forEach(function(o) {
var oItems = typeof o.items === 'string' ? JSON.parse(o.items) : (o.items || []);
html += '
';
html += '
';
html += '' + (o.user_name||'用户') + ' ';
html += '' + o.consume_code + ' ';
html += '
';
html += '
' + new Date(o.created_at).toLocaleString() + '
';
oItems.forEach(function(it) {
html += '
' + it.name + ' ×' + it.quantity + ' ¥' + (it.price*it.quantity).toFixed(2) + '
';
});
if (o.remark) html += '
备注:' + o.remark + '
';
html += '
';
html += '确认核销 ';
html += '🖨 打印 ';
html += '
';
html += '
';
});
}
html += '
';
// 已完成订单
html += '
';
html += '
✅ 已完成 (' + confirmed.length + ')
';
confirmed.slice(0, 10).forEach(function(o) {
html += '
';
html += '' + (o.user_name||'') + ' ¥' + parseFloat(o.total_price).toFixed(2) + ' ' + new Date(o.confirmed_at).toLocaleDateString() + ' ';
if (o.invoice_requested) html += ' 需开票 ';
html += '
';
});
html += '
';
// 商品管理入口
html += '
';
html += '
🏪 商品管理
';
html += '
管理商品/服务 ';
html += '
';
// 电脑端入口
html += '
';
html += '
💻 电脑端管理
';
html += '
在电脑浏览器打开以下地址,可管理店铺和打印清单
';
html += '
https://www.juanshixi.com/merchant.html
';
html += '
复制链接 ';
html += '
';
document.getElementById('merchantCenterContent').innerHTML = html;
}
function closeMerchantCenter() {
document.getElementById('merchantCenterOverlay').style.display = 'none';
}
function copyText(text) {
if (navigator.clipboard) {
navigator.clipboard.writeText(text).then(function() { if(typeof vant!=='undefined') vant.showToast('已复制'); });
} else {
var inp = document.createElement('input');
inp.value = text;
document.body.appendChild(inp);
inp.select();
document.execCommand('copy');
document.body.removeChild(inp);
if(typeof vant!=='undefined') vant.showToast('已复制');
}
}
function verifyOrder() {
var code = document.getElementById('verifyCodeInput').value.trim();
if (!code || code.length !== 6) { if(typeof vant!=='undefined') vant.showToast('请输入6位消费码'); return; }
var vm = window.__vueApp;
document.getElementById('verifyResult').innerHTML = '
查询中...
';
fetch('/api/merchant-orders/verify', {
method: 'POST',
headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer ' + vm.token},
body: JSON.stringify({consume_code: code})
}).then(function(r) { return r.json(); })
.then(function(d) {
if (d.error) {
document.getElementById('verifyResult').innerHTML = '
' + d.error + '
';
return;
}
var o = d.order;
var oItems = typeof o.items === 'string' ? JSON.parse(o.items) : (o.items || []);
var html = '
';
html += '
';
html += '✅ 消费码有效 ';
html += '' + o.consume_code + ' ';
html += '
';
html += '
用户:' + (o.user_name||'') + '
';
html += '
下单时间:' + new Date(o.created_at).toLocaleString() + '
';
html += '
';
oItems.forEach(function(it) {
html += '
';
html += '' + it.name + ' ×' + it.quantity + ' ';
html += '¥' + (it.price*it.quantity).toFixed(2) + ' ';
html += '
';
});
html += '
';
html += '合计 ¥' + parseFloat(o.total_price).toFixed(2) + ' ';
html += '
';
html += '
';
if (o.remark) html += '
备注:' + o.remark + '
';
html += '
确认核销 ';
html += '
';
document.getElementById('verifyResult').innerHTML = html;
})
.catch(function() {
document.getElementById('verifyResult').innerHTML = '
查询失败
';
});
}
function confirmOrderById(oid) {
var vm = window.__vueApp;
if (!confirm('确认核销此订单?')) return;
fetch('/api/merchant-orders/confirm', {
method: 'POST',
headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer ' + vm.token},
body: JSON.stringify({order_id: oid})
}).then(function(r) { return r.json(); })
.then(function(d) {
if (d.error) { if(typeof vant!=='undefined') vant.showToast(d.error); return; }
if(typeof vant!=='undefined') vant.showToast('核销成功');
// 刷新商家中心
openMerchantCenter();
})
.catch(function() { if(typeof vant!=='undefined') vant.showToast('核销失败'); });
}
function printReceipt(oid) {
// 生成消费清单图片供打印
var d = merchantCenterData;
if (!d || !d.orders) return;
var o = d.orders.find(function(x) { return x.id === oid; });
if (!o) return;
var items = typeof o.items === 'string' ? JSON.parse(o.items) : (o.items || []);
var c = document.createElement('canvas');
c.width = 300;
c.height = 350 + items.length * 30;
var ctx = c.getContext('2d');
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, c.width, c.height);
ctx.fillStyle = '#333';
ctx.font = 'bold 18px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('卷十息 · 消费清单', 150, 30);
ctx.font = '14px sans-serif';
ctx.textAlign = 'left';
ctx.fillText('店名:' + (d.merchant.name||''), 20, 60);
ctx.fillText('消费码:' + o.consume_code, 20, 82);
ctx.fillText('用户:' + (o.user_name||''), 20, 104);
ctx.fillText('下单时间:' + new Date(o.created_at).toLocaleString(), 20, 126);
ctx.fillText('─────────────────────', 20, 148);
var y = 170;
items.forEach(function(it) {
ctx.fillText(it.name + ' ×' + it.quantity, 20, y);
ctx.textAlign = 'right';
ctx.fillText('¥' + (it.price*it.quantity).toFixed(2), 280, y);
ctx.textAlign = 'left';
y += 28;
});
ctx.fillText('─────────────────────', 20, y);
y += 22;
ctx.font = 'bold 16px sans-serif';
ctx.fillText('合计', 20, y);
ctx.textAlign = 'right';
ctx.fillText('¥' + parseFloat(o.total_price).toFixed(2), 280, y);
ctx.textAlign = 'center';
ctx.font = '12px sans-serif';
ctx.fillStyle = '#999';
y += 24;
ctx.fillText('以到店实际价格为准 | 卷十息', 150, y);
var link = document.createElement('a');
link.download = '消费清单_' + o.consume_code + '.png';
link.href = c.toDataURL('image/png');
link.click();
}
// 商品管理(简易版)
function openItemManager() {
var items = shopItems.length ? shopItems : [];
var html = '
';
html += '
';
html += '商品/服务列表 ';
html += '+ 添加 ';
html += '
';
html += '
';
if (items.length === 0) {
html += '
暂无商品,点击添加
';
}
items.forEach(function(it) {
html += '
';
html += '
' + it.name + ' ¥' + parseFloat(it.price).toFixed(2) + '
';
html += '
';
html += '删除 ';
html += '
';
});
html += '
';
html += '
';
html += '
← 返回商家中心 ';
document.getElementById('merchantCenterContent').innerHTML = html;
// 加载最新商品列表
var vm = window.__vueApp;
var mid = merchantCenterData.merchant.id;
fetch('/api/merchants/' + mid + '/items', {headers: {'Authorization': 'Bearer ' + vm.token}})
.then(function(r) { return r.json(); })
.then(function(d) {
shopItems = d.items || [];
// 重新渲染
var listHtml = '';
if (shopItems.length === 0) {
listHtml = '
暂无商品,点击添加
';
}
shopItems.forEach(function(it) {
listHtml += '
';
listHtml += '
' + it.name + ' ¥' + parseFloat(it.price).toFixed(2) + ' ';
if (it.category) listHtml += ' ' + it.category + ' ';
listHtml += '
';
listHtml += '
删除 ';
listHtml += '
';
});
var listEl = document.getElementById('itemList');
if (listEl) listEl.innerHTML = listHtml;
});
}
function addItemForm() {
var html = '
';
html += '
添加商品/服务
';
html += '
';
html += '
';
html += '
';
html += '
';
html += '
添加 ';
html += '
';
document.getElementById('addItemArea').innerHTML = html;
}
function submitNewItem() {
var vm = window.__vueApp;
var mid = merchantCenterData.merchant.id;
var name = document.getElementById('newItemName').value.trim();
var price = document.getElementById('newItemPrice').value;
var cat = document.getElementById('newItemCat').value.trim();
var desc = document.getElementById('newItemDesc').value.trim();
if (!name) { if(typeof vant!=='undefined') vant.showToast('请输入名称'); return; }
if (!price) { if(typeof vant!=='undefined') vant.showToast('请输入价格'); return; }
fetch('/api/merchants/' + mid + '/items', {
method: 'POST',
headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer ' + vm.token},
body: JSON.stringify({name: name, price: price, category: cat, description: desc})
}).then(function(r) { return r.json(); })
.then(function(d) {
if (d.error) { if(typeof vant!=='undefined') vant.showToast(d.error); return; }
if(typeof vant!=='undefined') vant.showToast('添加成功');
openItemManager(); // 刷新
})
.catch(function() { if(typeof vant!=='undefined') vant.showToast('添加失败'); });
}
function deleteItem(iid) {
if (!confirm('确定删除?')) return;
var vm = window.__vueApp;
var mid = merchantCenterData.merchant.id;
fetch('/api/merchants/' + mid + '/items/' + iid, {
method: 'DELETE',
headers: {'Authorization': 'Bearer ' + vm.token}
}).then(function(r) { return r.json(); })
.then(function(d) {
if(typeof vant!=='undefined') vant.showToast('已删除');
openItemManager();
})
.catch(function() { if(typeof vant!=='undefined') vant.showToast('删除失败'); });
}
// === 修改大市场商家卡片:点击进入店铺 ===
var _origRenderMerchants = renderMerchants;
renderMerchants = function() {
if (!marketMerchants.length) {
document.getElementById('marketList').innerHTML = '
';
return;
}
var catMap = {eat:'吃🍜',wear:'穿👕',live:'住🏠',use:'用🛒',go:'行🚗'};
var html = '';
marketMerchants.forEach(function(m) {
var dist = m.distance ? (m.distance < 1000 ? Math.round(m.distance) + 'm' : (m.distance/1000).toFixed(1) + 'km') : '';
html += '
';
html += '
';
html += '
' + (m.name||'') + '
';
if (m.verified) html += '
已认证 ';
html += '
';
html += '
';
html += '' + (catMap[m.category]||m.category||'') + ' ';
if (dist) html += '📍' + dist + ' ';
if (m.avg_price) html += '人均¥' + parseFloat(m.avg_price).toFixed(0) + ' ';
if (m.rating) html += '⭐' + parseFloat(m.rating).toFixed(1) + ' ';
html += '
';
if (m.address) html += '
📍 ' + m.address + '
';
if (m.description) html += '
' + m.description + '
';
html += '
查看店铺 ›
';
html += '
';
});
document.getElementById('marketList').innerHTML = html;
};
// === 查看我的订单 ===
function openMyOrders() {
var vm = window.__vueApp;
if (!vm || !vm.token) { if(typeof vant!=='undefined') vant.showToast('请先登录'); return; }
document.getElementById('consumeCodeOverlay').style.display = 'flex';
document.getElementById('consumeCodeContent').innerHTML = '
加载中...
';
fetch('/api/merchant-orders/my', {headers: {'Authorization': 'Bearer ' + vm.token}})
.then(function(r) { return r.json(); })
.then(function(d) {
var orders = d.orders || [];
var statusMap = {pending:'待到店', confirmed:'已核销', cancelled:'已取消', expired:'已过期'};
var statusColor = {pending:'#ff9800', confirmed:'#07c160', cancelled:'#999', expired:'#999'};
var html = '
我的订单
';
if (orders.length === 0) {
html += '
暂无订单
';
}
orders.forEach(function(o) {
var items = typeof o.items === 'string' ? JSON.parse(o.items) : (o.items || []);
html += '
';
html += '
';
html += '' + (o.merchant_name||'') + ' ';
html += '' + (statusMap[o.status]||o.status) + ' ';
html += '
';
items.forEach(function(it) {
html += '
' + it.name + ' ×' + it.quantity + '
';
});
html += '
';
html += '¥' + parseFloat(o.total_price).toFixed(2) + ' ';
html += '' + new Date(o.created_at).toLocaleDateString() + ' ';
html += '
';
if (o.status === 'pending') {
html += '
消费码:' + o.consume_code + '
';
}
html += '
';
});
document.getElementById('consumeCodeContent').innerHTML = html;
})
.catch(function() {
document.getElementById('consumeCodeContent').innerHTML = '
加载失败
';
});
}